﻿using Engine;
using Engine.AddinManager;
using Engine.FileFormats3D;
using Engine.Graphics;
using Engine.Graphics.VertexStructures;
using SharpDX;
using street.GameDllInterfaces;
using System;
using System.Collections.Generic;

namespace street
{
    internal class CreateRoadMesh
    {
        #region Public Methods

        public static Mesh3dData CreateSimpleRoad(Vector3[] middle, decimal anglePointOne, decimal anglePointTwo,
             float widthPointStart, float widthPointEnd, float inclinationPointStart, float inclinationMiddle, float inclinationPointEnd,
            float textureUcoordinate, Material materialID_ForRoad, Engine.AddinManager.AddinManager addinManager,
            IAddin addin, IStreetAddin gameVertexCreationAddin, DataForGamevertexCalclation data)
        {
            Vector3 normal;
            List<int> polyTextures = new List<int>();
            List<ushort> indices = new List<ushort>();
            List<MyOwnVertex.PositionNormalTextured> vertices = new List<MyOwnVertex.PositionNormalTextured>();
            List<IGameVertex> verticesWr2Data = new List<IGameVertex>();
            short OffsetIndices = (short)(vertices.Count);

            polyTextures.Add((middle.Length - 1) * 4);

            float lengthOfStreet = 0.0f;
            // This calculates the distances between each Vectors from middleline
            float[] middleLineDistances = MiddleLine.CalculateDistanceBetweenSegments(middle, out lengthOfStreet);
            float[] textureUcoordinates = new float[middleLineDistances.Length];
            //lengthOfStreet *= 0.5f;

            // here will the indicies calculate
            short point = 0;
            for (short i2 = 0; i2 < (middle.Length - 1); i2++)
            {
                indices.Add((ushort)(point + OffsetIndices));
                indices.Add((ushort)(point + 1 + OffsetIndices));
                indices.Add((ushort)(point + 4 + OffsetIndices));

                indices.Add((ushort)(point + 1 + OffsetIndices));
                indices.Add((ushort)(point + 2 + OffsetIndices));
                indices.Add((ushort)(point + 5 + OffsetIndices));

                indices.Add((ushort)(point + OffsetIndices));
                indices.Add((ushort)(point + 4 + OffsetIndices));
                indices.Add((ushort)(point + 3 + OffsetIndices));

                indices.Add((ushort)(point + 1 + OffsetIndices));
                indices.Add((ushort)(point + 5 + OffsetIndices));
                indices.Add((ushort)(point + 4 + OffsetIndices));
                point += 3;
            }

            // Create Texture V-Coordinates
            for (int i = 0; i < middle.Length; i++)
            {
                textureUcoordinates[i] = textureUcoordinate;
                textureUcoordinate += middleLineDistances[i] / 100;
            }

            Vector3 rightPoint; //Is a tempoary Point
            float currentDistanceOnRoad = 0;
            float streetwidth;
            float inclination = 0;

            //start
            rightPoint = GeoMath.MakePointFromAngel(180 - anglePointOne, widthPointEnd, inclinationPointEnd);
            normal = GeoMath.Normal2Poly(middle[1], middle[0] - rightPoint, middle[0] + rightPoint);

            // Left Point
            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[0] - rightPoint,
                Normal = normal,
                UV = new Vector2(0.0f, textureUcoordinates[0])
            });

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[0],
                Normal = normal,
                UV = new Vector2(-0.5f, textureUcoordinates[0])
            });

            // Right Point
            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[0] + rightPoint,
                Normal = normal,
                UV = new Vector2(-1.0f, textureUcoordinates[0])
            });

            for (int i = 1; i < (middle.Length - 1); i++)
            {
                currentDistanceOnRoad += middleLineDistances[i - 1];
                streetwidth = widthPointEnd - ((widthPointEnd - widthPointStart) * (currentDistanceOnRoad / lengthOfStreet));

                if (currentDistanceOnRoad / lengthOfStreet < 0.50f)
                    inclination = inclinationPointEnd + ((inclinationMiddle - inclinationPointEnd) * (currentDistanceOnRoad * 2.0f / lengthOfStreet));
                else
                    inclination = inclinationMiddle - ((inclinationPointStart - inclinationMiddle) * (1.0f - (currentDistanceOnRoad * 2.0f / lengthOfStreet)));

                float currentDistanceOnRoadInPercent = (currentDistanceOnRoad / lengthOfStreet);
                rightPoint = GeoMath.makepoint(middle[i + 1], middle[i - 1], streetwidth, inclination);

                normal = GeoMath.Normal2Poly(middle[i + 1], middle[i] - rightPoint, middle[i] + rightPoint);
                normal += GeoMath.Normal2Poly(middle[i - 1], middle[i] + rightPoint, middle[i] - rightPoint);
                normal.Normalize();

                // Left vertex
                vertices.Add(new MyOwnVertex.PositionNormalTextured()
                {
                    Position = middle[i] - rightPoint,
                    Normal = normal,
                    UV = new Vector2(0.0f, textureUcoordinates[i])
                });

                vertices.Add(new MyOwnVertex.PositionNormalTextured()
                {
                    Position = middle[i],
                    Normal = normal,
                    UV = new Vector2(-0.5f, textureUcoordinates[i])
                });

                vertices.Add(new MyOwnVertex.PositionNormalTextured()
                {
                    Position = middle[i] + rightPoint,
                    Normal = normal,
                    UV = new Vector2(-1.0f, textureUcoordinates[i])
                });
            }

            rightPoint = GeoMath.MakePointFromAngel(180 - anglePointTwo - 180, widthPointStart, inclinationPointStart);
            normal = GeoMath.Normal2Poly(middle[middle.Length - 2], middle[middle.Length - 1] + rightPoint, middle[middle.Length - 1] - rightPoint);

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[middle.Length - 1] - rightPoint,
                Normal = normal,
                UV = new Vector2(0.0f, textureUcoordinates[textureUcoordinates.Length - 1])
            });

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[middle.Length - 1],
                Normal = normal,
                UV = new Vector2(-0.5f, textureUcoordinates[textureUcoordinates.Length - 1])
            });

            vertices.Add(new MyOwnVertex.PositionNormalTextured()
            {
                Position = middle[middle.Length - 1] + rightPoint,
                Normal = normal,
                UV = new Vector2(-1.0f, textureUcoordinates[textureUcoordinates.Length - 1])
            });

            if (gameVertexCreationAddin != null)
                verticesWr2Data = gameVertexCreationAddin.CreateGameVertices(middle, vertices, lengthOfStreet, middleLineDistances, textureUcoordinates, data);
            else
                throw new NotImplementedException("Addin could not create gameVertices for roadmesh.");

            if (vertices.Count != verticesWr2Data.Count)
                throw new Exception("The number of vertices for road and gamevertices data must be the same!");

            List<Material> materialIDs = new List<Material>();
            materialIDs.Add(materialID_ForRoad);
            List<EnumMappingMode> mappingMode = new List<EnumMappingMode>();
            mappingMode.Add(EnumMappingMode.material32);

            return new Mesh3dData(indices, vertices, verticesWr2Data, polyTextures, new List<Material>() { materialID_ForRoad }, addinManager, addin);
        }

        #endregion Public Methods
    }
}